import tkinter as tk
from tkinter import ttk, messagebox, simpledialog, filedialog
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


# 学生课程类
class Course:
    def __init__(self, course_name, credits, grade, is_repeated=False):
        self.course_name = course_name
        self.credits = credits
        self.grade = grade
        self.is_repeated = is_repeated

    def __str__(self):
        return f"课程名称：{self.course_name}, 学分：{self.credits}, 成绩：{self.grade}, 是否重修：{self.is_repeated}"


# 学生基本信息类
class StudentInfo:
    def __init__(self, student_id, name, department, class_name):
        self.student_id = student_id
        self.name = name
        self.department = department
        self.class_name = class_name


# 学生学业数据类
class AcademicData:
    def __init__(self):
        self.courses_taken = []  # 已修课程列表
        self.gpa = 0.0  # 平均学分绩点
        self.failed_credits = 0  # 不及格课程总学分
        self.warning_status = "无"  # 学业警示状态
        self.total_credits_required = 120  # 毕业所需总学分（假设值）
        self.earned_credits = 0  # 已获学分
        self.retention_count = 0  # 留级次数

    def add_course(self, course):
        # 检查课程是否已存在
        existing_course = next((c for c in self.courses_taken if c.course_name == course.course_name), None)
        if existing_course:
            existing_course.credits = course.credits
            existing_course.grade = course.grade
            existing_course.is_repeated = course.is_repeated
        else:
            self.courses_taken.append(course)
        self.update_gpa()
        self.update_failed_credits()
        self.update_earned_credits()
        self.update_warning_status()

    def update_gpa(self):
        total_points = 0
        total_credits = 0
        for course in self.courses_taken:
            if course.grade >= 60:  # 假设 60 分为及格
                if course.grade >= 90:
                    points = 4.0
                elif course.grade >= 80:
                    points = 3.0
                elif course.grade >= 70:
                    points = 2.0
                elif course.grade >= 60:
                    points = 1.0
                else:
                    points = 0.0
                total_points += points * course.credits
                total_credits += course.credits
        if total_credits > 0:
            self.gpa = total_points / total_credits
        else:
            self.gpa = 0.0

    def update_failed_credits(self):
        self.failed_credits = sum(course.credits for course in self.courses_taken if course.grade < 60)

    def update_earned_credits(self):
        self.earned_credits = sum(course.credits for course in self.courses_taken if course.grade >= 60)

    def update_warning_status(self):
        if self.gpa < 1.67 and self.failed_credits >= 20:
            self.warning_status = "退学警示"
        elif self.gpa < 1.67:
            self.warning_status = "学习警示"
        elif self.gpa < 2.00:
            self.warning_status = "预警谈话"
        else:
            self.warning_status = "无"

    def check_retention(self):
        # 判断是否留级（假设已获学分低于总学分的 50% 则留级）
        if self.earned_credits < self.total_credits_required * 0.5:
            self.retention_count += 1  # 增加留级次数
            return True
        return False

    def check_dropout(self):
        # 条件 1：连续两个学期 GPA < 1.67 且累计不及格学分 ≥ 30
        if self.gpa < 1.67 and self.failed_credits >= 30:
            return True
        # 条件 2：留级次数 ≥ 3 次
        if self.retention_count >= 3:
            return True
        # 条件 3：其他特殊情形（暂未实现）
        return False


# 学生类
class Student:
    def __init__(self, student_info, academic_data):
        self.student_info = student_info
        self.academic_data = academic_data

    def __str__(self):
        return (f"学号：{self.student_info.student_id}, 姓名：{self.student_info.name}, "
                f"院系：{self.student_info.department}, 班级：{self.student_info.class_name}, "
                f"GPA：{self.academic_data.gpa:.2f}, 不及格学分：{self.academic_data.failed_credits}, "
                f"已获学分：{self.academic_data.earned_credits}, 警示状态：{self.academic_data.warning_status}")


# 学生管理类
class StudentManager:
    def __init__(self):
        self.students = {}  # 学生信息字典，以学号为键

    def add_student(self, student):
        self.students[student.student_info.student_id] = student

    def get_student_info(self, student_id):
        return self.students.get(student_id, None)

    def add_course_to_student(self, student_id, course):
        student = self.get_student_info(student_id)
        if student:
            student.academic_data.add_course(course)

    def export_to_excel(self, file_path):
        data = []
        for student in self.students.values():
            for course in student.academic_data.courses_taken:
                data.append([
                    student.student_info.student_id,
                    student.student_info.name,
                    student.student_info.department,
                    student.student_info.class_name,
                    course.course_name,
                    course.credits,
                    course.grade,
                    "是" if course.is_repeated else "否"
                ])
        df = pd.DataFrame(data, columns=["学号", "姓名", "院系", "班级", "课程名称", "学分", "成绩", "是否重修"])
        df.to_excel(file_path, index=False)


# 图形化界面类
class StudentGUI:
    def __init__(self, master):
        self.master = master
        self.master.title("学生学情管理系统")
        self.master.geometry("800x600")
        self.master.configure(bg="#f0f0f0")
        self.student_manager = StudentManager()

        # 创建分组框
        self.input_frame = ttk.LabelFrame(master, text="学生信息输入", padding=10)
        self.input_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew")

        self.query_frame = ttk.LabelFrame(master, text="查询学生信息", padding=10)
        self.query_frame.grid(row=1, column=0, padx=10, pady=5, sticky="ew")

        self.action_frame = ttk.LabelFrame(master, text="操作", padding=10)
        self.action_frame.grid(row=2, column=0, padx=10, pady=5, sticky="ew")

        self.result_frame = ttk.LabelFrame(master, text="查询结果", padding=10)
        self.result_frame.grid(row=3, column=0, padx=10, pady=5, sticky="nsew")

        # 学生信息输入区域
        self.id_label = ttk.Label(self.input_frame, text="学号:")
        self.id_label.grid(row=0, column=0, padx=5, pady=5)
        self.id_entry = ttk.Entry(self.input_frame)
        self.id_entry.grid(row=0, column=1, padx=5, pady=5)

        self.name_label = ttk.Label(self.input_frame, text="姓名:")
        self.name_label.grid(row=1, column=0, padx=5, pady=5)
        self.name_entry = ttk.Entry(self.input_frame)
        self.name_entry.grid(row=1, column=1, padx=5, pady=5)

        self.department_label = ttk.Label(self.input_frame, text="院系:")
        self.department_label.grid(row=2, column=0, padx=5, pady=5)
        self.department_entry = ttk.Entry(self.input_frame)
        self.department_entry.grid(row=2, column=1, padx=5, pady=5)

        self.class_label = ttk.Label(self.input_frame, text="班级:")
        self.class_label.grid(row=3, column=0, padx=5, pady=5)
        self.class_entry = ttk.Entry(self.input_frame)
        self.class_entry.grid(row=3, column=1, padx=5, pady=5)

        # 查询学生信息区域
        self.query_id_label = ttk.Label(self.query_frame, text="查询学号:")
        self.query_id_label.grid(row=0, column=0, padx=5, pady=5)
        self.query_id_entry = ttk.Entry(self.query_frame)
        self.query_id_entry.grid(row=0, column=1, padx=5, pady=5)

        self.query_button = ttk.Button(self.query_frame, text="查询学生", command=self.query_student)
        self.query_button.grid(row=0, column=2, padx=5, pady=5)

        # 操作按钮区域
        self.add_button = ttk.Button(self.action_frame, text="添加学生", command=self.add_student)
        self.add_button.grid(row=0, column=0, padx=5, pady=5)

        self.add_grade_button = ttk.Button(self.action_frame, text="录入成绩", command=self.add_grade)
        self.add_grade_button.grid(row=0, column=1, padx=5, pady=5)

        self.import_button = ttk.Button(self.action_frame, text="导入表格", command=self.import_from_file)
        self.import_button.grid(row=0, column=2, padx=5, pady=5)

        self.export_button = ttk.Button(self.action_frame, text="导出表格", command=self.export_to_file)
        self.export_button.grid(row=0, column=3, padx=5, pady=5)

        self.clear_button = ttk.Button(self.action_frame, text="清空输入", command=self.clear_entries)
        self.clear_button.grid(row=0, column=4, padx=5, pady=5)

        self.trend_button = ttk.Button(self.action_frame, text="学业趋势", command=self.show_trend)
        self.trend_button.grid(row=0, column=5, padx=5, pady=5)

        # 查询结果显示区域
        self.result_text = tk.Text(self.result_frame, height=10, width=70)
        self.result_text.grid(row=0, column=0, padx=5, pady=5)

        # 配置网格布局权重
        master.grid_rowconfigure(3, weight=1)
        master.grid_columnconfigure(0, weight=1)

    def add_student(self):
        student_id = self.id_entry.get()
        name = self.name_entry.get()
        department = self.department_entry.get()
        class_name = self.class_entry.get()

        if student_id and name and department and class_name:
            student_info = StudentInfo(student_id, name, department, class_name)
            academic_data = AcademicData()
            student = Student(student_info, academic_data)
            self.student_manager.add_student(student)
            messagebox.showinfo("提示", "学生信息添加成功！")
        else:
            messagebox.showerror("错误", "请输入完整的学生信息！")

    def add_grade(self):
        student_id = self.id_entry.get()
        student = self.student_manager.get_student_info(student_id)
        if student:
            course_name = simpledialog.askstring("输入", "课程名称:")
            credits = simpledialog.askinteger("输入", "学分:")
            grade = simpledialog.askinteger("输入", "成绩:")
            is_repeated = simpledialog.askinteger("输入", "是否重修(0-否, 1-是):")
            if course_name and credits and grade is not None and is_repeated is not None:
                course = Course(course_name, credits, grade, bool(is_repeated))
                self.student_manager.add_course_to_student(student_id, course)
                messagebox.showinfo("提示", "学生成绩录入成功！")
        else:
            messagebox.showerror("错误", "未找到该学生信息！")

    def query_student(self):
        student_id = self.query_id_entry.get()
        student = self.student_manager.get_student_info(student_id)
        if student:
            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, str(student))
            self.result_text.insert(tk.END, "\n已修课程信息：\n")
            for course in student.academic_data.courses_taken:
                self.result_text.insert(tk.END, str(course) + "\n")

            # 判断是否留级或退学
            if student.academic_data.check_retention():
                self.result_text.insert(tk.END, "\n警告：该学生需要留级！\n")
            if student.academic_data.check_dropout():
                self.result_text.insert(tk.END, "\n警告：该学生需要退学！\n")
        else:
            messagebox.showerror("错误", "未找到该学生信息！")

    def import_from_file(self):
        file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")])
        if file_path:
            self.import_students_from_excel(file_path)
            messagebox.showinfo("提示", "学生信息已从表格中导入成功！")

    def import_students_from_excel(self, file_path):
        df = pd.read_excel(file_path)
        for index, row in df.iterrows():
            student_id = str(row['学号'])
            name = row['姓名']
            department = row['院系']
            class_name = row['班级']
            if student_id not in self.student_manager.students:
                student_info = StudentInfo(student_id, name, department, class_name)
                academic_data = AcademicData()
                student = Student(student_info, academic_data)
                self.student_manager.add_student(student)
            else:
                student = self.student_manager.get_student_info(student_id)

            course_name = row['课程名称']
            course_credits = row['课程学分']
            course_grade = row['成绩']
            course_is_repeated = row['是否重修'] == '是'
            course = Course(course_name, course_credits, course_grade, course_is_repeated)
            self.student_manager.add_course_to_student(student_id, course)

    def export_to_file(self):
        file_path = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx")])
        if file_path:
            self.student_manager.export_to_excel(file_path)
            messagebox.showinfo("提示", f"学生信息已导出到 {file_path}")

    def clear_entries(self):
        self.id_entry.delete(0, tk.END)
        self.name_entry.delete(0, tk.END)
        self.department_entry.delete(0, tk.END)
        self.class_entry.delete(0, tk.END)
        self.query_id_entry.delete(0, tk.END)
        self.result_text.delete(1.0, tk.END)

    def show_trend(self):
        student_id = self.query_id_entry.get()
        student = self.student_manager.get_student_info(student_id)
        if student:
            grades = [course.grade for course in student.academic_data.courses_taken]
            if grades:
                fig, ax = plt.subplots()
                ax.plot(grades, marker='o')
                ax.set_title(f"{student.student_info.name} 的学业趋势")
                ax.set_xlabel("课程")
                ax.set_ylabel("成绩")
                ax.grid(True)

                # 创建新窗口显示图表
                trend_window = tk.Toplevel(self.master)
                trend_window.title("学业趋势分析")
                canvas = FigureCanvasTkAgg(fig, master=trend_window)
                canvas.draw()
                canvas.get_tk_widget().pack()
            else:
                messagebox.showerror("错误", "该学生没有足够的课程数据进行分析！")
        else:
            messagebox.showerror("错误", "未找到该学生信息！")


if __name__ == "__main__":
    root = tk.Tk()
    app = StudentGUI(root)
    root.mainloop()